package com.file.parser;

import com.file.bo.ResponseData;
import com.file.bo.mail.CCB;
import com.file.bo.mail.CCBTran;
import com.file.constant.EfpConstants;
import com.file.constant.ErrorCode;
import com.file.parser.traprange.PDFTableExtractor;
import com.file.util.JsonUtils;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class CCBPdfParser extends BasePdfParser {

	public ResponseData<String> parseCCBPdfToJson(String filePath) {
		log.info("parseCCBPdfToJson started, filePath:{}", filePath);
		String json = null;

		try {
			CCB ccb = parseCCBPdf(filePath);
			json = JsonUtils.convertObjectToJson(ccb);
		} catch (Exception e) {
			log.error("[OnError]parseCCBPdfToJson failed, filePath:{}", filePath, e);
			return new ResponseData<String>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(),
					ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
		}

		log.info("parseCCBPdfToJson completed, filePath:{}", filePath);
		return new ResponseData<String>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
	}

	private CCB parseCCBPdf(String filePath) {
		CCB ccb = parseCCBHeader(filePath);

		List<CCBTran> ccbTrans = parseCcbTrans(filePath);
		try {
			ccbTrans = parseCcbLastTwoColumns(filePath, ccbTrans);
		} catch (Exception e) {
			log.warn("parseCcbLastTwoColumns failed", e);
		}
		ccb.setCcbTrans(ccbTrans);

		return ccb;
	}

	private CCB parseCCBHeader(String filePath) {
		CCB ccb = new CCB();
		String pdfHeaderText = parsePdfHeaderText(filePath);
		String accountNo = pdfHeaderText.substring(pdfHeaderText.indexOf("卡号/账号:") + 6, pdfHeaderText.indexOf("客户名称："))
				.trim();
		String name = pdfHeaderText.substring(pdfHeaderText.indexOf("客户名称：") + 5, pdfHeaderText.indexOf("起始日期："))
				.trim();
		String startDate = pdfHeaderText.substring(pdfHeaderText.indexOf("起始日期：") + 5, pdfHeaderText.indexOf("结束日期："))
				.trim();
		String endDate = pdfHeaderText.substring(pdfHeaderText.indexOf("结束日期：") + 5, pdfHeaderText.indexOf("序号 摘要"))
				.trim();

		ccb.setAccountNo(accountNo);
		ccb.setName(name);
		ccb.setStartDate(startDate);
		ccb.setEndDate(endDate);

		return ccb;
	}

	private List<CCBTran> parseCcbTrans(String filePath) {
		List<CCBTran> ccbTrans = new ArrayList<>();

		String transText = parseTransToText(filePath);
		if (Strings.isNullOrEmpty(transText)) {
			return ccbTrans;
		}

		List<List<String>> tranFieldsList = parseTransTextToTranFieldsList(transText);

		for (List<String> strings : tranFieldsList) {
			if (!Strings.isNullOrEmpty(strings.get(6))) {
				CCBTran ccbTran = new CCBTran();
				ccbTran.setId(strings.get(0));
				ccbTran.setTransactionType(strings.get(1));
				ccbTran.setCurrency(strings.get(2));
				ccbTran.setCashRemit(strings.get(3));
				ccbTran.setTransactionDate(strings.get(4));
				ccbTran.setTransactionAmount(strings.get(5));
				ccbTran.setBalance(strings.get(6));
				ccbTran.setTransactionComment(strings.get(7));
				ccbTran.setCounterParty(strings.get(8));
				ccbTrans.add(ccbTran);
			}
		}

		return ccbTrans;
	}

	// 实际只有交易地点、附言字段汉字超过15个，或者
	private List<CCBTran> parseCcbLastTwoColumns(String filePath, List<CCBTran> ccbTrans) {
		String pdfText = getPdfTextByStripper(filePath);
		pdfText = pdfText.replace(System.getProperty("line.separator", "\n"), " ");

		// 此处一行的格式:1 支付机构提现 人民币元 钞 20210426 10.00 10.00 余额宝提现 215500690/支付宝（中国）网络技术有限公司
		Pattern pattern1 = Pattern.compile("\\d{1,5}\\s\\S{1,16}\\s\\S{1,5}\\s\\S{1,3}\\s\\d{8}\\s");
		Matcher matcher1 = pattern1.matcher(pdfText);

		// 每行交易记录的第一个字所在的index
		int tranIndex = 0;
		Map<Integer, Integer> tranTextStartIndexMap = new HashMap<>();

		while (matcher1.find()) {
//			log.info(
//			"Match \"" + matcher1.group() + "\" at positions " + matcher1.start() + "-" + (matcher1.end() - 1));
			tranTextStartIndexMap.put(tranIndex, matcher1.start());
			tranIndex++;
		}

		// 找出每行交易记录的文本
		List<String> tranTextList = new ArrayList<>();
		for (int i = 1; i < tranTextStartIndexMap.size(); i++) {
			String tranText = pdfText.substring(tranTextStartIndexMap.get(i - 1), tranTextStartIndexMap.get(i));
			if (tranText.contains("生成时间:")) {
				tranText = tranText.substring(0, tranText.indexOf("生成时间:")).trim();
			}
			tranTextList.add(tranText.trim());
		}

		String lastTranText = pdfText.substring(tranTextStartIndexMap.get(tranTextStartIndexMap.size() - 1));
		lastTranText = lastTranText.substring(0, lastTranText.indexOf("生成时间:")).trim();
		tranTextList.add(lastTranText);

		for (int i = 0; i < ccbTrans.size(); i++) {
			List<String> ccbTranFields = Splitter.on(" ").splitToList(tranTextList.get(i));
			// 这里大部分通过trapegrange后两个字段都是好的，好的就可以不用设置，只有遇到特殊情况比如字段太长换行才需要重新设置
//			//前提假设是前7个字段不为空，且是必填,且文本不换行， 因为换行则会多出现一个空格
//			if (ccbTranFields.size() == 7) {
//				//后两个字段附言和对方为空
//				continue;
//			} 
//			if (ccbTranFields.size() == 8) {
//				//字段附言或对方为空，没有出现换行，因为换行会多出空格导致size变大，不换行则不需处理
//				continue;
//			}
			if (ccbTranFields.size() == 9) {
				// 大概率是字段附言或对方都有值，且无换行，但是也有可能是因为其中一个字段有值，且有换行，处理这种特殊情况
				// 假设是附言为空，则合并换行的两个字符串set给对手字段
				if (StringUtils.isBlank(ccbTrans.get(i).getTransactionComment())) {
					ccbTrans.get(i).setCounterParty(ccbTranFields.get(7).trim() + ccbTranFields.get(8).trim());
				}
				// 假设是对手字段为空，则合并换行的两个字符串set给附言字段
				if (StringUtils.isBlank(ccbTrans.get(i).getCounterParty())) {
					ccbTrans.get(i).setTransactionComment(ccbTranFields.get(7).trim() + ccbTranFields.get(8).trim());
				}
			}
			if (ccbTranFields.size() == 10) {
				// 大概率是字段附言或对方都有值,且字段附言或对方其中一个出现换行
				if (StringUtils.equals(ccbTranFields.get(7), ccbTrans.get(i).getTransactionComment())) {
					// 附言字段正确，则换行出现在对手字段
					ccbTrans.get(i).setCounterParty(ccbTranFields.get(8).trim() + ccbTranFields.get(9).trim());
				} else {
					// 对手字段正确，则换行出现在附言字段
					ccbTrans.get(i).setTransactionComment(ccbTranFields.get(7).trim() + ccbTranFields.get(8).trim());
				}
			}
			if (ccbTranFields.size() == 11) {
				// 大概率是字段附言或对方都有值,且字段附言和对方均出现出现换行
				ccbTrans.get(i).setTransactionComment(ccbTranFields.get(7).trim() + ccbTranFields.get(8).trim());
				ccbTrans.get(i).setCounterParty(ccbTranFields.get(9).trim() + ccbTranFields.get(10).trim());
			}
		}

		return ccbTrans;
	}

	private String parseTransToText(String filePath) {
		PDFTableExtractor extractor = (new PDFTableExtractor()).setSource(filePath);
		int pdfPageNumber = getPdfPageNumber(filePath);

		for (int k = 0; k < pdfPageNumber; k++) {
			int[] skipLinesIndexes = new int[EfpConstants.CCB_PDF_HEADER_LINE_NUMBER
					+ EfpConstants.CCB_PDF_FOOTER_LINE_NUMBER];
			for (int i = 0; i < EfpConstants.CCB_PDF_HEADER_LINE_NUMBER; i++) {
				skipLinesIndexes[i] = i;
			}
			for (int j = 0; j < EfpConstants.CCB_PDF_FOOTER_LINE_NUMBER; j++) {
				skipLinesIndexes[EfpConstants.CCB_PDF_HEADER_LINE_NUMBER + j] = -(j + 1);
			}
			extractor.exceptLine(k, skipLinesIndexes);
		}

		return extractPdfToText(extractor);
	}

	public static void main(String[] args) {
		CCBPdfParser ccbPdfParser = new CCBPdfParser();
//		CCB ccb = ccbPdfParser.parseCCBPdf("E:\\data\\files\\CCB\\建设银行.pdf");
//		CCB ccb = ccbPdfParser.parseCCBPdf("E:\\data\\files\\CCB\\建行流水.pdf");
		CCB ccb = ccbPdfParser.parseCCBPdf("E:\\data\\files\\CCB\\建设银行流水.pdf");

		String json = JsonUtils.convertObjectToJson(ccb);
		System.out.println(json);
	}

}
